
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Threading;
using Swaf.Container;
using Swaf.Logging;

namespace Swaf.BizRule.ThreadedQueue
{
	/// <summary>
	/// Summary description for Worker.
	/// </summary>
	public class Worker
	{
		protected RuleQueue m_parentQueue;
		private Thread m_workThread;
		private bool m_continueToWork = true;
		private IRuleGroupControl m_currentRuleGroup;
		protected internal IRunningRule m_currentRunningRule = null;
		private LocalDataStoreSlot m_cacheThreadSlot = null;
		protected IList m_expiredCache = new ArrayList();
		
#if(!COMPACT_FRAMEWORK)
		public Worker(RuleQueue queue, ApartmentState comThreadType)
		{
			m_parentQueue = queue;
			m_workThread = new Thread(new ThreadStart(processWork));
			m_workThread.ApartmentState = comThreadType;
			m_workThread.Name = "RuleQueueWorker:" + Guid.NewGuid().ToString();
			m_workThread.Start();
		}
#endif

		public Worker(RuleQueue queue)
		{
			m_parentQueue = queue;
			m_workThread = new Thread(new ThreadStart(processWork));
#if(!COMPACT_FRAMEWORK)
			m_workThread.Name = "RuleQueueWorker:" + Guid.NewGuid().ToString();
#endif
			m_workThread.Start();
		}

		protected void processExpiredList(FlexiMap cache)
		{
			lock(m_expiredCache)
			{
				foreach(IDisposable content in m_expiredCache)
				{
					try
					{
						object key = null;

						// Get the key to the content
						foreach (DictionaryEntry entry in cache)
						{
							if (entry.Value == content)
							{
								key = entry.Key;
								break;
							}
						}

						// If found the key, remove the corresponding item
						if (key != null)
						{
							cache.Remove(key);
						}

						// Dispose of the content
						content.Dispose();
					}
					catch(Exception e)
					{
						Application.currentApp.Trace.write(TraceLevels.Verbose, "Unable to dispose of expired cache item {0}.  Error was {1}", content.GetType().Name,e.ToString());
					}
				}

				m_expiredCache.Clear();
			}
		}
		private void Worker_NodeExpiring(object contents, ref bool cancel)
		{
			if (contents is IDisposable)
			{
				lock(m_expiredCache)
				{
					m_expiredCache.Add(contents);
					cancel = true;
				}
			}
		}
		protected void processWork()
		{
			Thread.SetData(m_parentQueue.WorkerSlot, this);
			//Create a globally available cache map in the thread local storage.
			LocalDataStoreSlot cacheSlot = Thread.GetNamedDataSlot(BasicExecutor.CACHE_THREAD_SLOT_NAME);
			FlexiMap cache = new FlexiMap();
			cache.NodeExpiring +=new ExpirationNotification(Worker_NodeExpiring);
			Thread.SetData(cacheSlot, cache);
			while(m_continueToWork)
			{
				//Reseting the currentRuleset to null just before getting the next
				//piece of work to do allows this thread to move between rulesets when
				//not excuting a rule within the context of another rule.
				processSingleElement(true);
				if(m_currentRuleGroup != null)
				{
					m_currentRuleGroup = CurrentRequest.removeWorker(this);
				}

				//See if any items cached in the thread local cache have expired and need disposing.
				//We do this here to handle any thread safe expiration issues.
				if (m_expiredCache.Count > 0)
				{
					processExpiredList(cache);
				}
			}
		}

		internal void processSingleElement(bool waitForWork)
		{
			QueueElement el = null;
			try
			{
				el = m_parentQueue.dequeue(this, waitForWork);
				if(el != null)
				{
					Cmn.PerfMon.incrementCounter("CurrentWorkersRunning");
					m_currentRuleGroup = (IRuleGroupControl)el.ruleGroup;
					el.execute(this);
					Cmn.PerfMon.decrementCounter("CurrentWorkersRunning");
				}
			}
#if(!COMPACT_FRAMEWORK)
			catch(ThreadAbortException e)
			{
			}
			catch(ThreadInterruptedException e)
			{
			}
#endif
			catch(Exception e)
			{
				string s = e.ToString();
			}
		}

		public void stopProcessing(bool interuptWork)
		{
			m_continueToWork = false;
#if(!COMPACT_FRAMEWORK)
			if(interuptWork)
				m_workThread.Abort();
#endif
		}


		internal IRuleGroupControl CurrentRuleGroup {get{return m_currentRuleGroup;}}
		internal BaseInitialRequest CurrentRequest {get{return CurrentRuleGroup==null?null:(BaseInitialRequest)CurrentRuleGroup.Group.MainRequest;}}
		internal IRunningRule CurrentRunningRule {get{return m_currentRunningRule;}}

	}
}
